﻿/*==============================================
JEU DE LA VIE
ALGORITHME SEQUENTIEL
Author : Sébastien Dez & Arnaud Villejobert
==============================================*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>

#include "JDLV_pthread.h"

pthread_mutex_t verrou = PTHREAD_MUTEX_INITIALIZER;

/*==============================================
METHODES
==============================================*/

//Rempli un morceau de cellules avec valeurs aléatoires (0 morte, 1 vivante)
Morceau initialiserMorceau(){
	Morceau morceauTmp;
	int i=0;
	for(i; i<M_SIZE; i++){
		int j=0;
		for(j; j<M_SIZE; j++){
			int randNum = rand()%(100-1 + 1) + 1;
			if(randNum > P_VIVANT_INIT){
				morceauTmp.listeCellules[i][j]=0;
			}else{
				morceauTmp.listeCellules[i][j]=1;
			}
		}
	}
	return morceauTmp;
}

//Affiche les cellules d'un morceau dans la console (0 morte, 1 vivante)
void afficherMorceau(Morceau morceauTmp){
	int i=0;
	for(i; i<M_SIZE; i++){
		int j=0;
		for(j; j<M_SIZE; j++){
			printf("%d",morceauTmp.listeCellules[i][j]);
		}
		printf("\n");
	}
}

//Rempli un domaine de morceaux
Domaine initialiserDomaine(){
	Domaine domaineTmp;
	int i=0;
	for(i; i<D_SIZE; i++){
		int j=0;
		for(j; j<D_SIZE; j++){
			domaineTmp.listeMorceaux[i][j]=initialiserMorceau();
		}
	}
	return domaineTmp;
}

//Affiche la ligne précise k d'un morceau m
void afficherLigneMorceau(Morceau morceauTmp, int ligneK){
	int j=0;
	for(j; j<M_SIZE; j++){
		printf("%d",morceauTmp.listeCellules[ligneK][j]);
	}
}

//Affiche les morceaux d'un domaine dans la console
void afficherMorceaux(Domaine domaineTmp){
	int i=0;
	for(i; i<D_SIZE; i++){
		int k=0;
		for(k; k<M_SIZE; k++){
			int j=0;
			for(j; j<D_SIZE; j++){
				Morceau m = domaineTmp.listeMorceaux[i][j];
				afficherLigneMorceau(m,k);
			}
			printf("\n");
		}
	}
}

//Retourne un int qui correspond au nombre de cellules vivantes autour de la cellule courante
int calculerNombreCellulesVivantesVoisines(int celluleA, int celluleB, int celluleC, 
											int celluleD, int celluleF, int celluleG, int celluleH, int celluleI){
	return celluleA + celluleB + celluleC + celluleD + celluleF + celluleG +celluleH +celluleI;
}

//Renvoie true si la cellule est vivante (1) false sinon
bool estVivante(int cellule){
	if(cellule==1){
		return true;
	}else{
		return false;
	}
}

//Détermine si une cellule meurt ou vie
//Fonction des cellules aux alentours
// A B C
// D E F
// G H I
//E EST LA CELLULE COURANTE A TRAITER
int evolutionCellule(int celluleA, int celluleB, int celluleC, 
					 int celluleD, int celluleE, int celluleF,
					 int celluleG, int celluleH, int celluleI){
	int nbCellulesVivantes = calculerNombreCellulesVivantesVoisines(celluleA,celluleB,celluleC,celluleD,celluleF,celluleG,celluleH,celluleI);
	if(estVivante(celluleE)){//cellule vivante
		if(nbCellulesVivantes==2 || nbCellulesVivantes==3){ //CAS 1 : vivante entourée par 2 ou 3 vivantes > VIE
			return 1;
		}else if((nbCellulesVivantes<2) || (nbCellulesVivantes>3)){
		//CAS 3 : vivante avec 0-1 vivant > MORT
		//CAS 5 : vivante avec >3 vivantes >MORT
			return 0;
		}
	}else{//cellule déjà morte
		if(nbCellulesVivantes==3){ //CAS 2 : morte entourée par =3 vivantes > VIE
			return 1;
		}else if(nbCellulesVivantes<3){ //CAS 4 : mort avec <3 vivantes > MORT
			return 0;
		}else{ //Si aucun cas ne match, elle reste morte
			return 0;
		}
	}
	return 0;
}

//Retourne la valeur d'une cellule présente à X Y dans le morceau contenant
int obtenirValeurCellule(Morceau contenant, int x, int y){
	if(contenant.listeCellules[x][y] !=0 && contenant.listeCellules[x][y] !=1){//sortie domaine
		return 0;
	}else{
		return contenant.listeCellules[x][y];
	}
}

//Parcours les cellules d'un morceau donné pour les faire evoluer
//X,Y étant les coordonnées du morceau courant
Morceau parcourirCellules(Domaine *domaine, int x, int y){
    Morceau morceauTmp = (*domaine).listeMorceaux[x][y];
	//Pour chaque cellules
	Morceau sortie;
	int i=0;
	for(i; i<M_SIZE; i++){
		int j=0;
		for(j; j<M_SIZE; j++){
			// A B C
			// D E F
			// G H I
			int celluleA=0;
			int celluleB=0;
			int celluleC=0;
			int celluleD=0;
			int celluleF=0;
			int celluleG=0;
			int celluleH=0;
			int celluleI=0;
			
            //CAS 1 : si bord supérieur du morceau et qui n'est pas celui du (*domaine)
			if(i==0 && x!=0){
                //1.1 :Si on a bord gauche et qui n'est pas celui du (*domaine)
				if(j==0 && y!=0){
                    celluleA=obtenirValeurCellule((*domaine).listeMorceaux[x-1][y-1],M_SIZE-1,M_SIZE-1);
                    celluleB=obtenirValeurCellule((*domaine).listeMorceaux[x-1][y],M_SIZE-1,j);
                    celluleC=obtenirValeurCellule((*domaine).listeMorceaux[x-1][y],M_SIZE-1,j+1);
                    celluleD=obtenirValeurCellule((*domaine).listeMorceaux[x][y-1],i,M_SIZE-1);
					celluleF=obtenirValeurCellule(morceauTmp,i,j+1);
                    celluleG=obtenirValeurCellule((*domaine).listeMorceaux[x][y-1],i+1,M_SIZE-1);
					celluleH=obtenirValeurCellule(morceauTmp,i+1,j);
					celluleI=obtenirValeurCellule(morceauTmp,i+1,j+1);
				}else if(j==M_SIZE-1 && y!=D_SIZE-1){
                    //1.2:Si on a bord droit et qui n'est pas celui du (*domaine)
                    celluleA=obtenirValeurCellule((*domaine).listeMorceaux[x-1][y],M_SIZE-1,j-1);
                    celluleB=obtenirValeurCellule((*domaine).listeMorceaux[x-1][y],M_SIZE-1,j);
                    celluleC=obtenirValeurCellule((*domaine).listeMorceaux[x-1][y+1],M_SIZE-1,0);
					celluleD=obtenirValeurCellule(morceauTmp,i,j-1);
                    celluleF=obtenirValeurCellule((*domaine).listeMorceaux[x][y+1],i,0);
					celluleG=obtenirValeurCellule(morceauTmp,i+1,j-1);
					celluleH=obtenirValeurCellule(morceauTmp,i+1,j);
                    celluleI=obtenirValeurCellule((*domaine).listeMorceaux[x][y+1],i+1,0);
				}else{
					//1.3:Ni bord à gauche, ni de bord à droite, juste au dessus
                    celluleA=obtenirValeurCellule((*domaine).listeMorceaux[x-1][y],M_SIZE-1,j-1);
                    celluleB=obtenirValeurCellule((*domaine).listeMorceaux[x-1][y],M_SIZE-1,j);
                    celluleC=obtenirValeurCellule((*domaine).listeMorceaux[x-1][y],M_SIZE-1,j+1);
					celluleD=obtenirValeurCellule(morceauTmp,i,j-1);
					celluleF=obtenirValeurCellule(morceauTmp,i,j+1);
					celluleG=obtenirValeurCellule(morceauTmp,i+1,j-1);
					celluleH=obtenirValeurCellule(morceauTmp,i+1,j);
					celluleI=obtenirValeurCellule(morceauTmp,i+1,j+1);
				}
			}
            //CAS 2 : si bord inférieur du morceau et qui n'est pas celui du (*domaine)
			else if(i==M_SIZE-1 && x!=D_SIZE-1){
                //2.1:Si on a bord gauche et qui n'est pas celui du (*domaine)
				if(j==0 && y!=0){
                    celluleA=obtenirValeurCellule((*domaine).listeMorceaux[x][y-1],i-1,M_SIZE-1);
					celluleB=obtenirValeurCellule(morceauTmp,i-1,j);
					celluleC=obtenirValeurCellule(morceauTmp,i-1,j+1);
                    celluleD=obtenirValeurCellule((*domaine).listeMorceaux[x][y-1],i,M_SIZE-1);
					celluleF=obtenirValeurCellule(morceauTmp,i,j+1);
                    celluleG=obtenirValeurCellule((*domaine).listeMorceaux[x+1][y-1],0, M_SIZE-1);
                    celluleH=obtenirValeurCellule((*domaine).listeMorceaux[x+1][y],0,j);
                    celluleI=obtenirValeurCellule((*domaine).listeMorceaux[x+1][y],0,j+1);
				}else if(j==M_SIZE-1 && y!=D_SIZE-1){
                    //2.2:Si on a bord droit et qui n'est pas celui du (*domaine)
					celluleA=obtenirValeurCellule(morceauTmp,i-1,j-1);
					celluleB=obtenirValeurCellule(morceauTmp,i-1,j);
                    celluleC=obtenirValeurCellule((*domaine).listeMorceaux[x][y+1],i-1,0);
					celluleD=obtenirValeurCellule(morceauTmp,i,j-1);
                    celluleF=obtenirValeurCellule((*domaine).listeMorceaux[x][y+1],i,0);
                    celluleG=obtenirValeurCellule((*domaine).listeMorceaux[x+1][y],0,j-1);
                    celluleH=obtenirValeurCellule((*domaine).listeMorceaux[x+1][y],0,j);
                    celluleI=obtenirValeurCellule((*domaine).listeMorceaux[x+1][y+1],0,0);
				}else{
					//2.3:Ni bord à gauche, ni de bord à droite, juste en dessous
					celluleA=obtenirValeurCellule(morceauTmp,i-1,j-1);
					celluleB=obtenirValeurCellule(morceauTmp,i-1,j);
					celluleC=obtenirValeurCellule(morceauTmp,i-1,j+1);
					celluleD=obtenirValeurCellule(morceauTmp,i,j-1);
					celluleF=obtenirValeurCellule(morceauTmp,i,j+1);
                    celluleG=obtenirValeurCellule((*domaine).listeMorceaux[x+1][y],0,j-1);
                    celluleH=obtenirValeurCellule((*domaine).listeMorceaux[x+1][y],0,j);
                    celluleI=obtenirValeurCellule((*domaine).listeMorceaux[x+1][y],0,j+1);
				}
			}else if(j==0 && y!=0){
                //CAS 3.1 : uniquement un bord à gauche qui n'est pas celui du (*domaine)
                celluleA=obtenirValeurCellule((*domaine).listeMorceaux[x][y-1],i-1,M_SIZE-1);
				celluleB=obtenirValeurCellule(morceauTmp,i-1,j);
				celluleC=obtenirValeurCellule(morceauTmp,i-1,j+1);
                celluleD=obtenirValeurCellule((*domaine).listeMorceaux[x][y-1],i,M_SIZE-1);
				celluleF=obtenirValeurCellule(morceauTmp,i,j+1);
                celluleG=obtenirValeurCellule((*domaine).listeMorceaux[x][y-1],i+1,M_SIZE-1);
				celluleH=obtenirValeurCellule(morceauTmp,i+1,j);
				celluleI=obtenirValeurCellule(morceauTmp,i+1,j+1);
			}else if(j==M_SIZE-1 && y!=M_SIZE-1){
                //CAS 4.1 : uniquement un bord à gauche qui n'est pas celui du (*domaine)
				celluleA=obtenirValeurCellule(morceauTmp,i-1,j-1);
				celluleB=obtenirValeurCellule(morceauTmp,i-1,j);
                celluleC=obtenirValeurCellule((*domaine).listeMorceaux[x][y+1],i-1,0);
				celluleD=obtenirValeurCellule(morceauTmp,i,j-1);
                celluleF=obtenirValeurCellule((*domaine).listeMorceaux[x][y+1],i,0);
				celluleG=obtenirValeurCellule(morceauTmp,i+1,j-1);
				celluleH=obtenirValeurCellule(morceauTmp,i+1,j);
                celluleI=obtenirValeurCellule((*domaine).listeMorceaux[x][y+1],i+1,0);
			}
			else if(i!=0 && j!=0 && i!=M_SIZE-1 && j!=M_SIZE-1){
				//CAS 5.1 : Aucun bords
				//On est dans le morceau courant
				celluleA=obtenirValeurCellule(morceauTmp,i-1,j-1);
				celluleB=obtenirValeurCellule(morceauTmp,i-1,j);
				celluleC=obtenirValeurCellule(morceauTmp,i-1,j+1);
				celluleD=obtenirValeurCellule(morceauTmp,i,j-1);
				celluleF=obtenirValeurCellule(morceauTmp,i,j+1);
				celluleG=obtenirValeurCellule(morceauTmp,i+1,j-1);
				celluleH=obtenirValeurCellule(morceauTmp,i+1,j);
				celluleI=obtenirValeurCellule(morceauTmp,i+1,j+1);
			}
			int celluleE= obtenirValeurCellule(morceauTmp,i,j); // Cellule courante
			int nouvelleValeurCellule = evolutionCellule(celluleA,celluleB,celluleC,celluleD,celluleE,celluleF,celluleG,celluleH,celluleI);
			sortie.listeCellules[i][j]= nouvelleValeurCellule;
		}
	}
	return sortie;
}

//routine de changement des cellules
void *routine_jeuDeLaVie(void* arg){
    /*pthread_t id = pthread_self();
	pthread_mutex_lock(&verrou);
	
    argThread2 *param;
    param = (argThread2 *)arg; //
    int coupleX = 1;
    int coupleY = 0;
    Domaine *dEntree = (*param).dEntree;
	Domaine *dSortie = (*param).dSortie;
    printf("Je suis %d et je parcours en %d/%d\n", id, (*param).x, (*param).y);
    (*dSortie).listeMorceaux[coupleX][coupleY]=parcourirCellules(dEntree,coupleX,coupleY);
    pthread_mutex_unlock(&verrou);*/

    pthread_t id = pthread_self();
    pthread_mutex_lock(&verrou);
    argThread2* param = (argThread2*)arg;
    int i, j;
    i = param->x;
    j= param->y;
    Domaine *dEntree = param->dEntree;
    Domaine *dSortie = param->dSortie;

    printf("Je suis %d et je parcours en %d/%d\n", id, param->x,  param->y);
    (*dSortie).listeMorceaux[i][j]=parcourirCellules(dEntree,i,j);
    pthread_mutex_unlock(&verrou);
}

//Fait 귯luer les cellules
Domaine jeuDeLaVie(Domaine dEntree){
	//Pour chaque morceau du domaine
    Domaine yo;
	Domaine *domaineSortie = malloc(sizeof(Domaine));
    domaineSortie=&yo;
    pthread_t* t_thread[NB_MORCEAUX];
    argThread2 * param;
    param = malloc(NB_MORCEAUX*sizeof(argThread2));
	int k =0;
    int i=0;
    for(i; i<D_SIZE; i++){
		int j=0;
		for(j; j<D_SIZE; j++){

            (param+k)->dSortie = domaineSortie;
            (param+k)->dEntree = &dEntree;
            (param+k)->x=i;
            (param+k)->y = j;
			//Parcours cellules d'un morceau du domaine
            //printf("Je traite en i/j : %d/%d et je suis %d\n", (param+k)->x,(param+k)->y,k);
            pthread_create(&t_thread[k], NULL, routine_jeuDeLaVie, (void*) (param+k));
			k++;
        }

	}

    int m;
	/*Synchronisation des Threads*/
    for(m=0; m<NB_MORCEAUX; m++){
        pthread_join(t_thread[m], NULL);
    }
    free(param);
    domaineSortie=NULL;
    free(domaineSortie);
    return yo;
}

/*==============================================
MAIN
==============================================*/
//Decommenter pour tester, commenter pour compiler avec QT 
/*int main (int argc, char ** argv){
	printf("******************************* MAIN \n");
	Domaine dEntree = initialiserDomaine();
	int nbIterations = 0;
	int iterationsMax=10;
	while(nbIterations<iterationsMax){
		//Afficher le domaine
		printf("Sortie n°-%d\n", nbIterations);
		Domaine tampon = jeuDeLaVie(dEntree);
		dEntree=tampon;
		afficherMorceaux(dEntree);
		nbIterations++;
	}
	printf("******************************* END \n");
	return 0;
}*/
